<?php

/**
 * SigmaCMS - Content Management System
 *
 * Copyright (C) 2008 Pavol Biely
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    SigmaCMS
 * @author     Pavol Biely <pavolbiely@gmail.com>
 * @copyright  2008 Pavol Biely
 * @license    http://www.gnu.org/licenses/gpl.txt   GNU General Public License
 * @link       http://pabi3.com/
 * @since      0.1.0
 */

abstract class FTP
{
	/**
	 * Connection resource
	 *
	 * @var resource
	 */
	static private $connection;

	/**
	 * Check if the system is connected to FTP server
	 *
	 * @return boolean
	 */
	static protected function isConnected()
	{
		if (!self::$connection) {
			return false;
		}
		return true;
	}

	/**
	 * Opens an FTP connection
	 *
	 * @param array $options
	 * @return void
	 */
	static public function connect(array $options)
	{
		// hostname must be specified
		if (!isset($options['host'])) {
			throw new Sigma_Exception('Hostname must be defined');
		}

		// connection
		$host    = (string) $options['host'];
		$ssl     = (bool)   $options['ssl'];

		// port
		$port = abs(intval($options['port']));
		$port = $port == 0 ? 21 : $port;

		// timeout
		$timeout = abs(intval($options['timeout']));
		$timeout = $timeout == 0 ? 90 : $timeout;

		// connect via SSL?
		if ($ssl === true) {
			self::$connection = @ftp_ssl_connect($host, $port, $timeout);
		} else {
			self::$connection = @ftp_connect($host, $port, $timeout);
		}

		// connection timeout
		if (self::$connection === false) {
			throw new Sigma_Exception('Unable to connect on ' . $host . ' due to timeout.');
		}

		// authentification
		if (isset($options['username'], $options['password'])) {
			// log in
			if (!@ftp_login(self::$connection, $options['username'], $options['password'])) {
				throw new Sigma_Exception('Wrong username/password' . ($ssl===true ? ' or SSL is not implemented' : NULL) . '.');
			}

			// passive mode
			if ($options['passive']) {
				ftp_pasv(self::$connection, (bool) $options['passive']);
			}
		}
	}

	/**
	 * Creates a directory
	 *
	 * @param string $directory
	 * @return string|boolean
	 */
	static public function mkdir($directory)
	{
		return @ftp_mkdir(self::$connection, $directory);
	}

	/**
	 * Removes a directory
	 *
	 * @param string $directory
	 * @return boolean
	 */
	static public function rmdir($directory)
	{
		$directory = rtrim($directory, '/').'/';

		$list = self::scan($directory);
		if ($list !== false && count($list) > 0) {
			foreach ($list as $subdir) {
				if ($subdir != '.' && $subdir != '..') {
					if (!self::delete($directory.$subdir)) {
						self::rmdir($directory.$subdir);
					}
				}
			}
		}

		return @ftp_rmdir(self::$connection, $directory);
	}

	/**
	 * Deletes a file on the FTP server
	 *
	 * @param string $file
	 * @return boolean
	 */
	static public function delete($file)
	{
		return @ftp_delete(self::$connection, $file);
	}

	/**
	 * Renames a file or a directory on the FTP server
	 *
	 * @param string $oldname
	 * @param string $newname
	 * @return boolean
	 */
	static public function rename($oldname, $newname)
	{
		return ftp_rename(self::$connection, $oldname, $newname);
	}

	/**
	 * Downloads a file from the FTP server
	 *
	 * @param string $remote
	 * @param string $local
	 * @param integer $mode
	 * @param integer $resumePos
	 * @return boolean
	 */
	static public function download($remote, $local, $mode = FTP_ASCII, $resumePos = 0)
	{
		if (!self::isConnected()) {
			throw new Sigma_Exception('Connection not established. You cannot download.');
		} else if ($mode !== FTP_ASCII && $mode !== FTP_BINARY) {
			throw new Sigma_Exception('Wrong mode selected.');
		}

		return ftp_get(self::$connection, $local, $remote, $mode, $resumePos);
	}

	/**
	 * Uploads a file to the FTP server
	 *
	 * @param string $local
	 * @param string $remote
	 * @param integer $mode
	 * @param integer $resumePos
	 * @return boolean
	 */
	static public function upload($local, $remote, $mode = FTP_ASCII, $resumePos = 0)
	{
		if (!self::isConnected()) {
			throw new Sigma_Exception('Connection not established. You cannot upload.');
		} else if ($mode !== FTP_ASCII && $mode !== FTP_BINARY) {
			throw new Sigma_Exception('Wrong mode selected.');
		} else if (!is_file($local)) {
			throw new Sigma_Exception('Local file not found.');
		}

		// try to allocate a space
		ftp_alloc(self::$connection, filesize($local));

		return ftp_put(self::$connection, $remote, $local, $mode, $resumePos);
	}

	/**
	 * Set permissions on a file via FTP
	 *
	 * @param string $filename
	 * @param integer $mode
	 * @return integer|boolean
	 */
	static public function chmod($filename, $mode = 0777)
	{
		if (!self::isConnected()) {
			throw new Sigma_Exception('Connection not established. You cannot change CHMOD.');
		}

		return ftp_chmod(self::$connection, $mode, $filename);
	}

	/**
	 * Checks whether a file or directory exists
	 *
	 * @param string $remote
	 * @return boolean
	 */
	static public function fileExists($remote)
	{
		$scan = self::scan(dirname($remote));
		if (is_array($scan)) {
			if (in_array(basename($remote), $scan)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Returns the size of the given file
	 *
	 * @param string $remote
	 * @return integer|boolean
	 */
	static public function fileSize($remote)
	{
		$fileSize = ftp_size(self::$connection, $remote);
		if ($fileSize == -1) {
			return false;
		}
		return $fileSize;
	}

	/**
	 * Returns a list of files in the given directory
	 *
	 * @param string $directory
	 * @param boolean $raw
	 * @return array|boolean
	 */
	static public function scan($directory = NULL, $raw = false)
	{
		if (!self::isConnected()) {
			throw new Sigma_Exception('Connection not established. You cannot list directories.');
		}

		if (!$directory) {
			$directory = './';
		}

		if ($raw) {
			return ftp_rawlist(self::$connection, $directory);
		} else {
			return ftp_nlist(self::$connection, $directory);
		}
	}

	/**
	 * Changes to the parent directory
	 *
	 * @param string $directory
	 * @return boolean
	 */
	static public function cd($directory = NULL)
	{
		if (!self::isConnected()) {
			throw new Sigma_Exception('Connection not established. You cannot list directories.');
		}

		if ($directory === NULL) {
			return ftp_cdup(self::$connection);
		} else {
			return @ftp_chdir(self::$connection, $directory);
		}
	}

	/**
	 * Returns the current directory name
	 *
	 * @return string|boolean
	 */
	static public function pwd()
	{
		if (!self::isConnected()) {
			throw new Sigma_Exception('Connection not established. You cannot list directories.');
		}

		return ftp_pwd(self::$connection);
	}

	/**
	 * Disconnect from the FTP server
	 *
	 * @return void
	 */
	static public function disconnect()
	{
		if (!self::isConnected()) return;

		ftp_close(self::$connection);
	}
}
